package org.fing.edu.uy.esbadp.motor.utils;

import java.util.logging.Level;

import org.fing.edu.uy.esbadp.core.AdpFlowInterface;
import org.fing.edu.uy.esbadp.core.AdpFlowSYNC;
import org.fing.edu.uy.esbadp.core.AdpFlowTree;
import org.fing.edu.uy.esbadp.core.entity.GenericTreeNode;
import org.fing.edu.uy.esbadp.logger.LoggerADP;
import org.fing.edu.uy.esbadp.motor.comunication.AdmAdaptationComunication;
import org.fing.edu.uy.esbadp.validation.ValidateArgument;

public class ProcessTreeUtils {
	
	private static java.util.logging.Logger logger = LoggerADP.getLogger(AdmAdaptationComunication.class.getSimpleName());

	/**
	 * @param src - Estrategia la cual va a ser procesada en caso que la misma este usando servicios virtuales equivalentes,
	 * en dicho caso remplaza los nodos SYNC de los servicios equivalente por la estrategia que esta usando actualmente es Esb-Adaptative.
	 * @param serviceId - Id del servicio para el cual se va  a procesar la estrategia.
	 * @return Retorna un GenericTreeNode&#60;AdpFlowInterface&#62; luego del mismo ser remplazado por las estrategias actuales de los servicios equivalentes.
	 */
	public static GenericTreeNode<AdpFlowInterface> processTree(GenericTreeNode<AdpFlowInterface> src, long serviceId){
		processTreeRecursive(src, serviceId);
		return src;
	}
	
	private static void processTreeRecursive(GenericTreeNode<AdpFlowInterface> src, long serviceId){
		if(src.hasChildren()){
			for(int index = 0; index < src.getChildren().size(); index++) {
				GenericTreeNode<AdpFlowInterface> child = src.getChildAt(index);
				//si es necesario procesar el nodo, los procesamos
				if(processingRequired(child, serviceId)){
					AdpFlowSYNC syncNode = (AdpFlowSYNC)child.getData();
					GenericTreeNode<AdpFlowInterface> dest = getActualFlowForService(syncNode.getServiceId());
					if(ValidateArgument.isNotNull(dest)){
						//remplace SYNC for dest strategy
						src.addChildAt(index, dest);
						//remove SYNC node to add strategy dest
						src.removeChildAt(index + 1);
						//add link to child SYNC
						for(GenericTreeNode<AdpFlowInterface> childForchild : child.getChildren()){
							addLinkLeafToSyncChild(dest, childForchild);
						}
					}
				}
				processTreeRecursive(child, serviceId);
			}
		}
	}
	
	private static boolean processingRequired(GenericTreeNode<AdpFlowInterface> src, long serviceId) {
		if(ValidateArgument.isNotNull(src.getData())){
			if(src.getData() instanceof AdpFlowSYNC){
				AdpFlowSYNC syncNode = (AdpFlowSYNC)src.getData();
				return syncNode.getServiceId() != serviceId;
			}
		}
		return false;
	}

	private static GenericTreeNode<AdpFlowInterface> getActualFlowForService(long serviceId) {
		GenericTreeNode<AdpFlowInterface> treeNode = null; 
		try{
			AdpFlowTree flowTree = AdmAdaptationComunication.getActualAdpFlow(serviceId);
			treeNode = flowTree.getFlowTree();
		}catch (Exception ex) {
			logger.log(Level.SEVERE, "There is no registration for the tree service " + serviceId + ".", ex);
		}
		return treeNode;
	}
	
	private static void addLinkLeafToSyncChild(GenericTreeNode<AdpFlowInterface> dest, GenericTreeNode<AdpFlowInterface> childForchild) {
		if(dest.hasChildren()){
			for(GenericTreeNode<AdpFlowInterface> child : dest.getChildren()){
				addLinkLeafToSyncChild(child, childForchild);
			}
		}else{
			dest.addChild(childForchild);
		}
	}
}
